import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Server {
	private ServerSocket server = null;
	private int port = 5000;
	private ExecutorService scheduler;

	public Server() {
		try {
			server = new ServerSocket(port);
			System.out.println("TCP Server started on port: " + port);
			scheduler = Executors.newCachedThreadPool();
			ClientHandler handler = null;
			Socket client = null;
			int clientNum = 0;
			while (true) {
				client = server.accept();
				System.out.println("Connected with client" + (++clientNum)
						+ "(" + client.getInetAddress() + ":"
						+ client.getPort() + ")");
				handler = new ClientHandler(client, clientNum);
				scheduler.execute(handler);
			}
		} catch (IOException e) {
			System.out.println("Server broken down");
		}
	}

	public static void main(String[] args) throws Exception {
		new Server();
	}

	class ClientHandler implements Runnable {
		private BufferedReader br = null;
		private PrintStream dos = null;
		private String clientName = null;
		private Socket client = null;

		ClientHandler(Socket client, int clientNum) {
			this.client = client;
			this.clientName = "Client" + clientNum;
			try {
				br = new BufferedReader(new InputStreamReader(client
						.getInputStream()));
				dos = new PrintStream(client.getOutputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void run() {
			try {
				while (true) {
					// receive a number and increment it by 1
					String content = receiveMessage();
					if(content == null){
						System.out.println("Disconnected with " + clientName);
						break;
					}
					int number = Integer.parseInt(content);
					number++;

					// generate a random string in form
					String append = genRandomString();

					// send the generated result to the client
					sendMessage(number + " " + append);

					// receive the modified result from the client
					String ret = receiveMessage();
					if(ret == null){
						System.out.println("Disconnected with " + clientName);
						break;
					}

					// verify the result
					if (ret.equals(convert(append)))
						sendMessage("OK");
					else
						sendMessage("FAIL");

				}
			} catch (NumberFormatException ne) {
				System.out.println(clientName + " hasn't sent an integer");
			} catch (IOException e) {
				System.out.println("Disconnected with " + clientName);
			} finally {
				try {
					if (br != null)
						br.close();
					if (dos != null)
						dos.close();
					if (client != null)
						client.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}

		private String convert(String append) {
			char[] chars = append.toCharArray();
			for (int i = 0; i < chars.length; i++) {
				if (chars[i] >= 'a' && chars[i] <= 'z')
					chars[i] -= 32;
				else if (chars[i] >= 'A' && chars[i] <= 'Z')
					chars[i] += 32;
			}
			return new String(chars);
		}

		public String receiveMessage() throws IOException {
			char[] buffer = new char[1024];
			int len = br.read(buffer);
			if(len < 0)
				return null;
			String content = new String(buffer, 0, len);
			System.out.println(clientName + " -> Server: " + content);
			return content;
		}

		public void sendMessage(String content) {
			System.out.println("Server" + " -> " + clientName + ": " + content);
			dos.print(content);
		}

		private String genRandomString() {
			Random random = new Random();
			int numOfChars = random.nextInt(11) + 10;
			char[] numbersAndLetters = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
					.toCharArray();
			char[] randBuffer = new char[numOfChars];
			for (int i = 0; i < numOfChars; i++)
				randBuffer[i] = numbersAndLetters[random.nextInt(62)];
			return new String(randBuffer);
		}
	}
}
